

#include "Simulation.h"

#include "SDLview.h"
#include "Controls.h"
#include "Camera.h"
#include "Timer.h"

#include "SDL/SDL.h"
#include "SDL_image/SDL_image.h"


#include <Model.h>
#include <FracturedMesh.h>

#include "polygonizer3.h"


Simulation* Simulation::_instance = NULL;

Simulation* Simulation::Instance()
{
	if (_instance == NULL)
	{
		_instance = new Simulation;
	}
	return _instance;
}

Simulation::Simulation()
{
	// instantiate all the singletons on startup
	SDLview::Instance();
	Controls::Instance();
	Camera::Instance();
	TimerRegistry::Instance();
	
	LFT = new Timer;
	
	Camera::Instance()->InitObserver();
}



inline double norm2(double X, double Y, double n)
{
	return pow(pow(X, n) + pow(Y, n), 1.0/n);
}

struct LevelSetFunctor
{
	// Level set at value=0.0
	static double value(V3d P)
	{
		double X = P[0];
		double Y = P[1];
		double Z = P[2];

		
			//double A = sqrt(X*X + Z*Z) - 0.5;
			//double n = 2.0;
			//return norm2(abs(A), abs(Y), n) - 0.2;
		
		
		
		
			//return pow(pow(A, n) + pow(Y, n), 1.0/n) - Y;
		
			//return V3d(X, Y, Z).length() - 1.0;
		
			//return max(max(fabs(X), fabs(Y)), fabs(Z)) - 5.0;
			//double phi = atan2(Y, X);
		
			//double Z = P[2] / (cos(phi*6.0)+2.0);
			
			return X*X + Y*Y + Z*Z - 2.0;
		
			//double R = 3.0;
			//double r = 1.0;
			//double b = (R - sqrt(X*X+Y*Y));
			//return (b*b) + Z*Z - r*r;		
			//V3d A = P - V3f(-0.5, 0, 0);
			//V3d B = P - V3f(0.5, 0, 0);
			//V3d C = P - V3f(0.0, 0.5, 0);
			//return 1.0/A.length() + 1.5f/B.length() + 1.0/C.length() - 6.0f;
	}
};

void Simulation::preprocess()
{
	//m_fracturedMesh = new FracturedMesh();
	
	m_polygonizer = new Polygonizer(5);
	
	Box3f bbox;         
	bbox.min = V3f(-5,-5,-5);
	bbox.max = V3f(5, 5, 5);
		
	const float tolerance = 1.0e-6f;
	const float edgeScale = 0.03;
	m_polygonizer->polygonize<LevelSetFunctor>(bbox, tolerance, edgeScale);
		
}


void Simulation::mainLoop()
{
	preprocess();
	
	SDL_ShowCursor(SDL_DISABLE);
	setupOpenGL();
	
	bool endSim = false;
	LFT->start();
	
	bool growthPhase = true;
	LIT.start();
	
	while ( !endSim ) 
	{
		const float MAXIMUM_FRAME_RATE = 500.f;
		const float MINIMUM_FRAME_RATE = 60.f;
		
		//UINT = Update Interval		
		const float UINT = 0.5f / MAXIMUM_FRAME_RATE;
		
		//MCPF = MAX_CYCLES_PER_FRAME
		const float MCPF = (2.0f*MAXIMUM_FRAME_RATE / MINIMUM_FRAME_RATE);
		
		//Max update interval
		const float MAXI = MCPF*UINT;
		
		static double timeLeftOver = 0.0; 
		
		double iup = LFT->time() + timeLeftOver; //updateIterations
		if (iup > MAXI) iup = MAXI;
		
		while (iup > UINT) 
		{		
			iup -= UINT;																					

			UserAction action = Controls::Instance()->checkUserInput(Camera::Instance());
			
			if (action==QUIT) 
			{
				endSim = true;
				break;
			}
			
			if (action==STEP && (LIT.time()>0.01))
			{
				if (growthPhase)
				{
					for (int n=0; n<1; ++n)
					{
						m_polygonizer->grow<LevelSetFunctor>();
					}
					LIT.start();
				}
	
			
				Geometry* geo = const_cast<Geometry*>(&m_polygonizer->getGeometry());
				//printf("Num vertices: %d\n", geo->m_vertices.size());
			}
			
			// Render
			render(UINT);
		}
	}
}


void Simulation::drawGrid(float edgeLength, int numDivisions)
{
	const float dL = edgeLength/float(numDivisions);
	const float he = 0.5f*edgeLength;
	
	glBegin(GL_LINES);
	for (int i=0; i<=numDivisions; ++i)
	{
		float l = -he + dL*float(i);
		glVertex3f(-he, l, 0.f);
		glVertex3f( he, l, 0.f);
		glVertex3f( l,-he, 0.f);
		glVertex3f( l, he, 0.f);
	}	
	glEnd();
	
}


void Simulation::render(float timestep)
{
	Camera::Instance()->SetObserverProjection();
	
	const float position0[] = { 100.0, 100.0, 50.0, 0.0 };
	const float position1[] = { -100.0,  -100.0, -50.0, 0.0 };
	glLightfv(GL_LIGHT0, GL_POSITION, position0);
	glLightfv(GL_LIGHT1, GL_POSITION, position1);
	
	const float specular[4] = {0.9f, 0.9f, 0.9f, 1.f};
	const float shine[1] = { 30.0f };

	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, shine);
		
	//glEnable(GL_COLOR_MATERIAL);
	//glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); 
		
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	
	
	/* draw world */
	glPushAttrib(GL_ALL_ATTRIB_BITS);
		
	drawGrid(10.f, 10.f);	

	glColor4f(1,1,1,1);
	
	glDisable(GL_TEXTURE_2D);
	//if (m_diffuseTexture)
	//	m_diffuseTexture->Apply();
	
	Geometry* geo = const_cast<Geometry*>(&m_polygonizer->getGeometry());
	
	geo->compileDisplayList(false);
	geo->draw(false);
	geo->compileDisplayList(true);
	geo->draw(true);
		
	m_polygonizer->drawFronts();

	glPopAttrib();
	
	SDL_GL_SwapBuffers ();
	
}


void Simulation::setupOpenGL()
{
	glClearColor(0.5f, 0.6f, 0.7f, 1.f);
	
	glEnable(GL_DEPTH_TEST);
	glDepthMask(GL_TRUE);
	glShadeModel(GL_SMOOTH);	
	glEnable(GL_POLYGON_SMOOTH);
	glDisable(GL_BLEND);
	
	glDisable(GL_CULL_FACE);
	glCullFace(GL_BACK);
	
	glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
	glEnable(GL_LIGHT1);
	
	float ambient[]   = { 0.9f, 0.9f, 0.9f, 1.0 };
	float diffuse0[]  = { 0.8, 0.8, 1.0, 1.0 };
	float specular0[] = { 1.0, 1.0, 1.0, 1.0 };
	float diffuse1[]  = { 1.0, 1.0, 0.9, 1.0 };
	float specular1[] = { 1.0, 1.0, 1.0, 1.0 };
	
	glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
    glLightfv(GL_LIGHT0, GL_SPECULAR, specular0);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse0);
	
	glLightfv(GL_LIGHT1, GL_AMBIENT, ambient);
	glLightfv(GL_LIGHT1, GL_SPECULAR, specular1);
	glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse1);
	
	glFogi(GL_FOG_MODE, GL_EXP);
	glFogf(GL_FOG_DENSITY, 0.05f);
	//glEnable(GL_FOG);
	
	glLightModelf( GL_LIGHT_MODEL_LOCAL_VIEWER, GL_FALSE );
	glLightModelf( GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE );
	glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
	
}







